package fun.yao.common.log;

import fun.yao.common.Constant;
import fun.yao.common.uitls.CommonUtil;
import fun.yao.common.uitls.ConfigUtil;
import fun.yao.common.uitls.DateUtil;
import fun.yao.common.uitls.MsgUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 日志管理线程
 * @author csl
 */
public class LogManager extends Thread
{
    /** 单例 */
    private static LogManager instance = null;

    /** 日志写入的间隔时间 */
    public final static int WRITE_LOG_INV_TIME   = ConfigUtil.getConfigByInt("WRITE_LOG_INV_TIME", 800);
    /** 单个日志文件的大小 默认为10兆 */
    public final static int SINGLE_LOG_FILE_SIZE = ConfigUtil.getConfigByInt("SINGLE_LOG_FILE_SIZE", 1024*1024*10);

    /** 日志文件列表 */
    private Map<String,LogFileItem> htLogFileMap = new ConcurrentHashMap<String,LogFileItem>();

    /** 是否运行 */
    private boolean bIsRun = true ;

    /** 变长报文的敏感字段屏蔽列表 add by csl 2010.8.5 */
    private String[] arrSecretKey = null;

    /**单条日志最大长度,-1表示没有限制  add by ljl 20171227*/
    public final static int WRITE_SINGLE_LOG_MAX_LENG = ConfigUtil.getConfigByInt("WRITE_SINGLE_LOG_MAX_LENG", -1);

    /** 构造函数 */
    protected LogManager()
    {
        try
        {
            File file = new File(Constant.CFG_LOG_PATH);
            if ( file.exists() == false )
                file.mkdir();
        }catch(Exception e)
        {
            e.printStackTrace();
        }
    }

    /** 获得日志管理类单例 */
    public synchronized static LogManager getInstance()
    {
        if ( instance == null )
        {
            instance = new LogManager();
            instance.setName("Log");
            instance.start();
        }
        return instance;
    }

    /**
     * 检查是否需要过滤报文中敏感字段的值
     * @return
     */
    public boolean checkIsNeedFilterSecretKey()
    {
        if ( arrSecretKey == null )
        {
            String sCfgValue = ConfigUtil.getConfigByString("LOG_FILTER_KEY_S","").trim();
            if ( sCfgValue.length() > 0 )
                arrSecretKey = sCfgValue.split("\\,");
            else
                arrSecretKey = new String[0];
        }

        return arrSecretKey.length > 0 ? true : false ;
    }

    /**
     * 过滤k=v变长报文中，敏感字段的值
     * @param sLogMsg
     * @return
     */
    public String filterSecretKeyValue(String sLogMsg)
    {

        for ( int i = 0 ; arrSecretKey != null && i < arrSecretKey.length ; i++ )
        {
            String sKeyValue = MsgUtil.getMsgFieldValue(sLogMsg, arrSecretKey[i]);
            if ( sKeyValue.length() > 0 )
            {
                String sOldMsg = arrSecretKey[i] + Constant.MSG_SEPARATOR_VALUE + sKeyValue;
                String sNewValue = arrSecretKey[i] + Constant.MSG_SEPARATOR_VALUE +
                        CommonUtil.FILL("", '*', sKeyValue.getBytes().length, 'L');
                sLogMsg = MsgUtil.replaceString(sLogMsg, sOldMsg, sNewValue);
            }
        }

        return sLogMsg;
    }

    /**
     * 过滤日志长度
     * @param sLogMsg
     * @return
     */
    public String filterLogMsgLeng(String sLogMsg)
    {
        if(WRITE_SINGLE_LOG_MAX_LENG >-1  && sLogMsg.length() > WRITE_SINGLE_LOG_MAX_LENG)
        {
            return sLogMsg.substring(0,WRITE_SINGLE_LOG_MAX_LENG) + "......";
        }else
        {
            return sLogMsg;
        }
    }

    /**
     * 添加日志
     * @param sLogFileName  日志文件名称
     * @param sbLogMsg      日志内容
     */
    public void addLog(String sLogFileName, StringBuffer sbLogMsg)
    {
        //获得单个日志文件的信息
        LogFileItem lfi = htLogFileMap.get(sLogFileName);
        if ( lfi == null )
        {
            synchronized(this)
            {
                lfi = htLogFileMap.get(sLogFileName);
                if ( lfi == null )
                {
                    lfi = new LogFileItem();
                    lfi.sLogFileName = sLogFileName;
                    lfi.iNextWriteTime = System.currentTimeMillis() + WRITE_LOG_INV_TIME;
                    htLogFileMap.put(sLogFileName, lfi);
                }
            }
        }

        //同步单个文件的日志
        synchronized(lfi)
        {
            if ( lfi.currLogBuff == 'A' )
                lfi.alLogBufA.add(sbLogMsg);
            else
                lfi.alLogBufB.add(sbLogMsg);
        }
    }

    /** 线程方法 */
    public void run()
    {
        int i = 0 ;
        while ( bIsRun )
        {
            try
            {
                //等待一定时间
                Thread.sleep(200);

                //输出到文件
                flush(false);

                //重新获取日志级别
                if ( i++ % 100 == 0 )
                {
//            		Constant.CFG_LOG_LEVEL = CommUtil.getConfigByString("LOG_LEVEL","1,2");;
                    i = 1 ;
                }

            }catch(Exception e)
            {
                System.out.println("写日志错误，出错位置:");
                e.printStackTrace();
            }
        }
    }

    /** 关闭方法 */
    public void close()
    {
        bIsRun = false;
        try
        {
            flush(true);
        }catch(Exception e)
        {
            System.out.println("写日志错误，出错位置:");
            e.printStackTrace();
        }
    }


    /**
     * 输出缓存的日志到文件
     * @param bIsForce 是否强制将缓存中的日志输出到文件
     */
    private void flush(boolean bIsForce) throws IOException
    {
        long   iCurrTime = System.currentTimeMillis();
        Iterator<String> iter = htLogFileMap.keySet().iterator();
        while ( iter.hasNext() )
        {
            LogFileItem lfi = htLogFileMap.get(iter.next());
            if ( iCurrTime >= lfi.iNextWriteTime || bIsForce == true )
            {
                //获得需要进行输出的缓存列表
                ArrayList<StringBuffer> alWrtLog = null;
                synchronized(lfi)
                {
                    if ( lfi.currLogBuff == 'A' )
                    {
                        alWrtLog = lfi.alLogBufA;
                        lfi.currLogBuff = 'B';
                    }else
                    {
                        alWrtLog = lfi.alLogBufB;
                        lfi.currLogBuff = 'A';
                    }
                }

                //创建日志文件
                createLogFile(lfi);

                //输出日志
                int iWriteSize = writeToFile(lfi.sFullLogFileName,alWrtLog);
                lfi.iCurrLogSize += iWriteSize;

            }
        }
    }


    /**
     * 创建日志文件
     * @param lfi
     */
    private void createLogFile(LogFileItem lfi )
    {
        //当前系统日期
        String sCurrPCDate = DateUtil.getCalcDate(0,DateUtil.sdf_yyyy_MM_dd);

        //如果超过单个文件大小，则拆分文件
        if ( lfi.sFullLogFileName != null
                && lfi.sFullLogFileName.length() > 0
                && lfi.iCurrLogSize >= LogManager.SINGLE_LOG_FILE_SIZE )
        {
            File oldFile = new File(lfi.sFullLogFileName);
            if ( oldFile.exists() )
            {
                String sNewFileName = Constant.CFG_LOG_PATH + "/" + lfi.sLastPCDate + "/"
                        + lfi.sLogFileName + "_" + DateUtil.getCalcDate(0,DateUtil.sdf_yyyy_MM_dd) + "_" + DateUtil.getCalcDate(0,DateUtil.sdf_hhmmss) + ".log";
                File newFile = new File(sNewFileName);
                boolean flag = oldFile.renameTo(newFile);
                System.out.println("日志已自动备份为 " + newFile.getName() + ( flag ? "成功!" : "失败!" ) );
                lfi.sFullLogFileName = "";
                lfi.iCurrLogSize = 0;
            }
        }

        //创建文件
        if ( lfi.sFullLogFileName == null
                || lfi.sFullLogFileName.length() <= 0
                || lfi.sLastPCDate.equals(sCurrPCDate) == false )
        {
            String sDir = Constant.CFG_LOG_PATH + "/" + sCurrPCDate ;
            File file = new File(sDir);
            if ( file.exists() == false )
                file.mkdir();
            lfi.sFullLogFileName = sDir + "/" + lfi.sLogFileName + ".log";
            lfi.sLastPCDate = sCurrPCDate;

            file = new File(lfi.sFullLogFileName);
            if ( file.exists() )
                lfi.iCurrLogSize = file.length();
            else
                lfi.iCurrLogSize = 0;
        }

    }

    /**
     * 输出日志到文件
     * @param sFullFileName 完整的日志文件名称
     * @param sbLogMsg      日志文件内容
     * @return 返回输出内容大小
     */
    private int writeToFile(String sFullFileName, ArrayList<StringBuffer> sbLogMsg) throws IOException
    {
        int size = 0;
        OutputStream fout = null;
        try{
            fout = new FileOutputStream(sFullFileName, true);
            for ( int i = 0 ; i < sbLogMsg.size() ; i++ )
            {
                StringBuffer logMsg = sbLogMsg.get(i);
                byte[] tmpBytes = MsgUtil.StringToBytes(logMsg.toString());
                fout.write(tmpBytes);
                size += tmpBytes.length;
            }
            fout.flush();
            sbLogMsg.clear();
        }catch(Exception eeee){
            eeee.printStackTrace();
        }finally{
            if ( fout != null )
                fout.close();
        }
        return size;
    }


}
